package com.xiaoxi.exam.service;

import cn.xinzhili.xutils.core.ErrorCode;
import cn.xinzhili.xutils.core.FailureException;
import cn.xinzhili.xutils.core.http.Response;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaoxi.exam.bean.VerifyCodeBean;
import com.xiaoxi.exam.bean.enums.UserSource;
import com.xiaoxi.exam.bean.enums.UserStatus;
import com.xiaoxi.exam.bean.errorcode.UserErrorCode;
import com.xiaoxi.exam.bean.request.AddTelRequest;
import com.xiaoxi.exam.bean.request.UpdateUserInfoRequest;
import com.xiaoxi.exam.bean.request.VerifyCodeRequest;
import com.xiaoxi.exam.bean.response.MobileResponse;
import com.xiaoxi.exam.bean.response.OpenidResponse;
import com.xiaoxi.exam.bean.response.UserInfoResponse;
import com.xiaoxi.exam.bean.util.response.OpenIdWXResponse;
import com.xiaoxi.exam.dao.UserMapper;
import com.xiaoxi.exam.model.Account;
import com.xiaoxi.exam.model.User;
import com.xiaoxi.exam.model.UserExample;
import com.xiaoxi.exam.utils.HttpsUtils;
import com.xiaoxi.exam.utils.UserFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Slf4j
public class UserService {

  @Value("${app.url}")
  private String weChatUrl;
  @Value("${app.id}")
  private String appId;
  @Value("${app.secret}")
  private String appSecret;
  @Value("${app.sms.code.verify:SMS_137170064}")
  private String verifyTemplateCode;

  @Autowired
  private UserMapper userMapper;

  @Autowired
  private AccountService accountService;

  private static final ObjectMapper mapper = new ObjectMapper();


  public MobileResponse getMobileByOpenid(String openid) {
    User existUser = getExistUser(openid);
    MobileResponse response = new MobileResponse();
    response.setMobile(existUser.getTel());
    return response;
  }

  @Transactional
  public Response bindMobile(VerifyCodeRequest request) {
    User existUser = getExistUser(request.getOpenid());
    if (!Objects.equals(existUser.getTel(), request.getMobile()) || !Objects
        .equals(existUser.getVerifyCode(), request.getVerifyCode())) {
      return Response.instanceFail(UserErrorCode.VERIFY_CODE_ERROR);
    } else {
      existUser.setVerifyCode(null);
      existUser.setStatus(UserStatus.NORMAL);
      userMapper.updateByPrimaryKey(existUser);
      return Response.instanceSuccess();
    }
  }

  @Transactional
  public Response preBindMobile(AddTelRequest request) {
    User user = getExistUser(request.getOpenid());
    UserFactory.toUser(user, request);
    user.setStatus(UserStatus.BINDING);
    user.setTel(request.getMobile());
    if (userMapper.updateByPrimaryKey(user) != 1) {
      log.warn("发送验证码前插入数据失败 tel:{}", request.getMobile());
      throw new FailureException(ErrorCode.REQUEST_FAILED);
    }
    VerifyCodeBean verifyCodeBean = new VerifyCodeBean();
    verifyCodeBean.setCode(user.getVerifyCode());
    return Response.instanceSuccess();
  }

  public Response getOpenidByCode(String code) {
    Map<String, String> openIdMap = new HashMap<>();
    openIdMap.put("appid", appId);
    openIdMap.put("secret", appSecret);
    openIdMap.put("js_code", code);
    openIdMap.put("grant_type", "authorization_code");
    OpenidResponse response = new OpenidResponse();
    try {
      HttpResponse httpResponse = HttpsUtils.get(weChatUrl, new HashMap<>(), openIdMap);
      String result = EntityUtils.toString(httpResponse.getEntity());
      OpenIdWXResponse openIdWXResponse = mapper.readValue(result, OpenIdWXResponse.class);
      if (Objects.isNull(openIdWXResponse.getErrcode())) {
        response.setOpenid(openIdWXResponse.getOpenid());
        User userInfo = getUserInfo(openIdWXResponse.getOpenid());
        if (Objects.nonNull(userInfo)) {
          response.setAvatar(userInfo.getAvatar());
          response.setTel(userInfo.getTel());
          response.setNickName(userInfo.getNickName());
//          syncService.syncUser(userInfo.getId());
        } else {
          User user = new User();
          user.setOpenid(openIdWXResponse.getOpenid());
          user.setSource(UserSource.WECHAT);
          user.setStatus(UserStatus.NEW);
          long userId = userMapper.insertReturningId(user);
//          syncService.syncUser(userId);
        }
      } else if (openIdWXResponse.getErrcode() == 40029) {
        return Response.instanceFail(UserErrorCode.CODE_INVALID);
      } else if (openIdWXResponse.getErrcode() == 40163) {
        return Response.instanceFail(UserErrorCode.CODE_USED);
      }
    } catch (Exception e) {
      log.warn("get openId failed code:{}", code);
      e.printStackTrace();
      return Response.instanceFail(ErrorCode.REQUEST_FAILED);
    }
    return Response.instanceSuccess(response);
  }


  public User getUserInfo(String openId) {
    UserExample example = new UserExample();
    example.or().andOpenidEqualTo(openId).andDeletedAtIsNull();
    List<User> users = userMapper.selectByExample(example);
    if (!users.isEmpty()) {
      return users.get(0);
    } else {
      return null;
    }
  }

  public UserInfoResponse getUserInfoResponse(String openId) {
    User existUser = getExistUser(openId);
    UserInfoResponse response = new UserInfoResponse();
    Account accountByUserId = accountService.getAccountByUserId(existUser.getId());
    response.setBindToeflAccount(Objects.nonNull(accountByUserId));
    return response;
  }


  @Transactional
  public Response updateUserInfo(UpdateUserInfoRequest request) {
    User userInfo = getExistUser(request.getOpenid());
    userInfo.setAvatar(request.getAvatar());
    userInfo.setNickName(request.getNickName());
    if (userMapper.updateByPrimaryKey(userInfo) != 1) {
      throw new FailureException(ErrorCode.REQUEST_FAILED);
    }
    return Response.instanceSuccess();
  }


  public User getExistUser(String openId) {
    User userInfo = getUserInfo(openId);
    if (Objects.isNull(userInfo)) {
      throw new FailureException(UserErrorCode.USER_NOT_EXIST);
    }
    return userInfo;
  }
}
